<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>声纹识别系统</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/lamejs@1.2.0/lame.all.js"></script>
    <link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
    <style>
        .container { max-width: 800px; margin-top: 30px; }
        .card { margin-bottom: 20px; }
        .result { margin-top: 20px; padding: 15px; border-radius: 5px; }
        .success { background-color: #d1e7dd; color: #0f5132; }
        .error { background-color: #f8d7da; color: #842029; }
        .warning { background-color: #fff3cd; color: #664d03; }
        .recorder-container { margin-top: 15px; }
        .recorder-controls { display: flex; gap: 10px; margin-bottom: 10px; }
        .audio-preview { margin-top: 10px; padding: 15px; background-color: #f8f9fa; border-radius: 5px; }
        .btn-record { background-color: #dc3545; color: white; }
        .btn-stop { background-color: #ffc107; color: black; }
        .btn-send { background-color: #198754; color: white; }
        .recording-indicator { display: inline-block; width: 10px; height: 10px; background-color: #dc3545; border-radius: 50%; margin-right: 5px; animation: pulse 1.5s infinite; }
        @keyframes pulse { 0% { opacity: 1; } 50% { opacity: 0.4; } 100% { opacity: 1; } }
    </style>
</head>
<body>
    <div class="container">
        <h1 class="text-center mb-4">声纹识别系统</h1>

        <!-- 2. 注册声纹 -->
        <div class="card">
            <div class="card-header">
                <h5>2. 注册声纹特征</h5>
            </div>
            <div class="card-body">
                <div class="mb-3">
                    <label for="reg_feature_id" class="form-label">用户ID（必填）</label>
                    <input type="text" class="form-control" id="reg_feature_id" placeholder="例如: user_001">
                </div>
                <div class="mb-3">
                    <label for="reg_group_id" class="form-label">分组ID（可选）</label>
                    <input type="text" class="form-control" id="reg_group_id" placeholder="默认: my_group_001">
                </div>
                
                <!-- 录音控件 -->
                <div class="recorder-container">
                    <div class="recorder-controls">
                        <button id="reg_startBtn" class="btn btn-record" onclick="startRecording('reg')">
                            <i class="fa fa-microphone"></i> 开始录音
                        </button>
                        <button id="reg_stopBtn" class="btn btn-stop" onclick="stopRecording('reg')" disabled>
                            <i class="fa fa-stop"></i> 停止录音
                        </button>
                        <button id="reg_sendBtn" class="btn btn-send" onclick="registerFeature()" disabled>
                            <i class="fa fa-paper-plane"></i> 发送录音
                        </button>
                    </div>
                    <div id="reg_recordingStatus" class="text-muted mb-2 d-none">
                        <span class="recording-indicator"></span>正在录音...请对着麦克风说话
                    </div>
                    <div id="reg_audioPreview" class="audio-preview d-none">
                        <h6 class="mb-2">录音预览（可播放）</h6>
                        <audio id="reg_audioPlayer" controls class="w-full mb-2"></audio>
                        <div class="d-flex justify-content-between text-sm text-muted">
                            <span>格式: MP3</span>
                            <span>采样率: 16kHz | 位长: 16bit | 单声道</span>
                        </div>
                    </div>
                </div>
                
                <div id="reg_result" class="result d-none"></div>
            </div>
        </div>

        <!-- 3. 声纹验证 -->
        <div class="card">
            <div class="card-header">
                <h5>3. 1对1声纹验证</h5>
            </div>
            <div class="card-body">
                <div class="mb-3">
                    <label for="verify_feature_id" class="form-label">用户ID（必填）</label>
                    <input type="text" class="form-control" id="verify_feature_id" placeholder="例如: user_001">
                </div>
                <div class="mb-3">
                    <label for="verify_group_id" class="form-label">分组ID（可选）</label>
                    <input type="text" class="form-control" id="verify_group_id" placeholder="默认: my_group_001">
                </div>
                <div class="mb-3">
                    <label for="threshold" class="form-label">相似度阈值（可选）</label>
                    <input type="number" step="0.01" min="0" max="1" class="form-control" id="threshold" value="0.8">
                </div>
                
                <!-- 录音控件 -->
                <div class="recorder-container">
                    <div class="recorder-controls">
                        <button id="verify_startBtn" class="btn btn-record" onclick="startRecording('verify')">
                            <i class="fa fa-microphone"></i> 开始录音
                        </button>
                        <button id="verify_stopBtn" class="btn btn-stop" onclick="stopRecording('verify')" disabled>
                            <i class="fa fa-stop"></i> 停止录音
                        </button>
                        <button id="verify_sendBtn" class="btn btn-send" onclick="verifyFeature()" disabled>
                            <i class="fa fa-paper-plane"></i> 发送录音
                        </button>
                    </div>
                    <div id="verify_recordingStatus" class="text-muted mb-2 d-none">
                        <span class="recording-indicator"></span>正在录音...请对着麦克风说话
                    </div>
                    <div id="verify_audioPreview" class="audio-preview d-none">
                        <h6 class="mb-2">录音预览（可播放）</h6>
                        <audio id="verify_audioPlayer" controls class="w-full mb-2"></audio>
                        <div class="d-flex justify-content-between text-sm text-muted">
                            <span>格式: MP3</span>
                            <span>采样率: 16kHz | 位长: 16bit | 单声道</span>
                        </div>
                    </div>
                </div>
                
                <div id="verify_result" class="result d-none"></div>
            </div>
        </div>
    </div>

    <script>
        // 全局变量
        let audioContext;
        let mediaRecorder;
        let audioChunks = [];
        let currentType = null;

        // 初始化音频上下文
        function initAudioContext() {
            if (!audioContext) {
                try {
                    audioContext = new (window.AudioContext || window.webkitAudioContext)();
                } catch (error) {
                    console.error('无法初始化 AudioContext:', error);
                    showResult('reg_result', 'error', '您的浏览器不支持音频录制功能');
                    showResult('verify_result', 'error', '您的浏览器不支持音频录制功能');
                }
            }
        }

        // 开始录音
        function startRecording(type) {
            initAudioContext();
            currentType = type;
            
            // 重置状态
            audioChunks = [];
            
            // 更新UI状态
            document.getElementById(`${type}_recordingStatus`).classList.remove('d-none');
            document.getElementById(`${type}_audioPreview`).classList.add('d-none');
            document.getElementById(`${type}_startBtn`).disabled = true;
            document.getElementById(`${type}_stopBtn`).disabled = false;
            document.getElementById(`${type}_sendBtn`).disabled = true;

            // 请求麦克风权限
            navigator.mediaDevices.getUserMedia({ audio: true })
                .then(stream => {
                    // 创建音频源
                    const source = audioContext.createMediaStreamSource(stream);
                    
                    // 创建 MediaRecorder
                    mediaRecorder = new MediaRecorder(stream);
                    
                    // 监听数据可用事件
                    mediaRecorder.addEventListener('dataavailable', event => {
                        audioChunks.push(event.data);
                    });
                    
                    // 监听录音停止事件
                    mediaRecorder.addEventListener('stop', () => {
                        const audioBlob = new Blob(audioChunks, { type: 'audio/wav' });
                        processAudio(audioBlob, type);
                        stream.getTracks().forEach(track => track.stop());
                    });
                    
                    // 开始录音
                    mediaRecorder.start();
                })
                .catch(error => {
                    console.error('录音错误:', error);
                    showResult(`${type}_result`, 'error', `录音失败: ${error.message}`);
                    // 错误发生时重新启用录音按钮
                    resetButtons(type);
                    document.getElementById(`${type}_startBtn`).disabled = false;
                });
        }

        // 停止录音
        function stopRecording(type) {
            if (mediaRecorder && mediaRecorder.state !== 'inactive') {
                mediaRecorder.stop();
                document.getElementById(`${type}_recordingStatus`).classList.add('d-none');
                document.getElementById(`${type}_stopBtn`).disabled = true;
            }
        }

        // 处理音频
        function processAudio(audioBlob, type) {
            // 转换为16kHz采样率（示例函数，实际实现可能需要更复杂的处理）
            convertTo16kHz(audioBlob)
                .then(convertedBlob => {
                    // 创建音频URL并设置预览
                    const audioUrl = URL.createObjectURL(convertedBlob);
                    const audioPlayer = document.getElementById(`${type}_audioPlayer`);
                    audioPlayer.src = audioUrl;
                    
                    // 显示预览
                    document.getElementById(`${type}_audioPreview`).classList.remove('d-none');
                    document.getElementById(`${type}_sendBtn`).disabled = false;
                    
                    // 存储处理后的音频
                    window[`${type}AudioBlob`] = convertedBlob;
                })
                .catch(error => {
                    console.error('音频处理错误:', error);
                    showResult(`${type}_result`, 'error', `音频处理失败: ${error.message}`);
                    // 错误发生时重新启用录音按钮
                    resetButtons(type);
                    document.getElementById(`${type}_startBtn`).disabled = false;
                });
        }

        // 音频采样率转换（示例实现，实际应用可能需要更复杂的处理）
        function convertTo16kHz(audioBlob) {
            return new Promise((resolve, reject) => {
                // 实际项目中可能需要使用 AudioContext 或第三方库进行采样率转换
                // 这里简化处理，直接返回原始音频（实际应用中需要实现真正的采样率转换）
                resolve(audioBlob);
            });
        }

        // 创建分组
        function createGroup() {
            const groupId = document.getElementById('group_id').value.trim() || 'my_group_001';
            showResult('group_result', 'warning', '正在创建分组...');
            
            // 模拟API请求
            setTimeout(() => {
                // 实际应用中这里应该发送HTTP请求到后端API
                showResult('group_result', 'success', `分组 "${groupId}" 创建成功`);
            }, 1000);
        }

        // 注册声纹特征
        function registerFeature() {
            const featureId = document.getElementById('reg_feature_id').value.trim();
            const groupId = document.getElementById('reg_group_id').value.trim() || 'my_group_001';
            
            if (!featureId) {
                showResult('reg_result', 'error', '请输入用户ID');
                // 错误发生时重新启用录音按钮
                document.getElementById('reg_startBtn').disabled = false;
                return;
            }
            
            const audioBlob = window['regAudioBlob'];
            if (!audioBlob) {
                showResult('reg_result', 'error', '请先录制音频');
                // 错误发生时重新启用录音按钮
                document.getElementById('reg_startBtn').disabled = false;
                return;
            }
            
            showResult('reg_result', 'warning', '正在注册声纹特征...');
            document.getElementById('reg_startBtn').disabled = true;
            
            // 模拟API请求
            setTimeout(() => {
                // 实际应用中这里应该发送HTTP请求到后端API
                // 随机模拟成功或失败
                const isSuccess = Math.random() > 0.2; // 80%成功率
                
                if (isSuccess) {
                    showResult('reg_result', 'success', `用户 "${featureId}" 的声纹特征已成功注册到分组 "${groupId}"`);
                    document.getElementById('reg_sendBtn').disabled = true;
                } else {
                    showResult('reg_result', 'error', `注册失败：声纹质量不足，请重新录制`);
                    // 失败时重新启用录音按钮
                    document.getElementById('reg_startBtn').disabled = false;
                }
            }, 1500);
        }

        // 验证声纹特征
        function verifyFeature() {
            const featureId = document.getElementById('verify_feature_id').value.trim();
            const groupId = document.getElementById('verify_group_id').value.trim() || 'my_group_001';
            const threshold = parseFloat(document.getElementById('threshold').value) || 0.8;
            
            if (!featureId) {
                showResult('verify_result', 'error', '请输入用户ID');
                // 错误发生时重新启用录音按钮
                document.getElementById('verify_startBtn').disabled = false;
                return;
            }
            
            const audioBlob = window['verifyAudioBlob'];
            if (!audioBlob) {
                showResult('verify_result', 'error', '请先录制音频');
                // 错误发生时重新启用录音按钮
                document.getElementById('verify_startBtn').disabled = false;
                return;
            }
            
            showResult('verify_result', 'warning', '正在验证声纹...');
            document.getElementById('verify_startBtn').disabled = true;
            
            // 模拟API请求
            setTimeout(() => {
                // 实际应用中这里应该发送HTTP请求到后端API
                // 生成随机相似度结果
                const similarity = Math.random().toFixed(2);
                const isMatch = parseFloat(similarity) >= threshold;
                
                if (isMatch) {
                    showResult('verify_result', 'success', 
                        `验证成功！用户 "${featureId}" 的声纹相似度为 ${similarity}（阈值: ${threshold}）`);
                    document.getElementById('verify_sendBtn').disabled = true;
                } else {
                    showResult('verify_result', 'error', 
                        `验证失败！用户 "${featureId}" 的声纹相似度为 ${similarity}（阈值: ${threshold}）`);
                    // 失败时重新启用录音按钮
                    document.getElementById('verify_startBtn').disabled = false;
                }
            }, 1500);
        }

        // 显示结果信息
        function showResult(elementId, type, message) {
            const resultElement = document.getElementById(elementId);
            resultElement.textContent = message;
            
            // 移除所有状态类
            resultElement.classList.remove('success', 'error', 'warning', 'd-none');
            
            // 添加对应状态类
            resultElement.classList.add(type);
            
            // 如果是错误消息，添加抖动动画
            if (type === 'error') {
                resultElement.classList.add('animate-shake');
                setTimeout(() => {
                    resultElement.classList.remove('animate-shake');
                }, 1000);
            }
            
            // 5秒后自动隐藏结果
            setTimeout(() => {
                resultElement.classList.add('d-none');
            }, 5000);
        }

        // 重置按钮状态
        function resetButtons(type) {
            document.getElementById(`${type}_startBtn`).disabled = true;
            document.getElementById(`${type}_stopBtn`).disabled = true;
            document.getElementById(`${type}_sendBtn`).disabled = true;
            document.getElementById(`${type}_recordingStatus`).classList.add('d-none');
            document.getElementById(`${type}_audioPreview`).classList.add('d-none');
        }

        // 添加抖动动画样式
        const style = document.createElement('style');
        style.textContent = `
            @keyframes shake {
                0%, 100% { transform: translateX(0); }
                10%, 30%, 50%, 70%, 90% { transform: translateX(-5px); }
                20%, 40%, 60%, 80% { transform: translateX(5px); }
            }
            .animate-shake {
                animation: shake 0.5s ease-in-out;
            }
        `;
        document.head.appendChild(style);
    </script>
</body>
</html>